package com.chuangke.flow.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.chuangke.admin.data.builder.DeptDataBuilder;
import com.chuangke.admin.data.builder.RoleDataBuilder;
import com.chuangke.admin.data.builder.UserDataBuilder;
import com.chuangke.attachment.service.AttachmentService;
import com.chuangke.common.constant.CacheKeyConstants;
import com.chuangke.common.constant.Masks;
import com.chuangke.common.constant.SysRelationTypeConstants;
import com.chuangke.common.data.builder.DataBuildConfig;
import com.chuangke.common.data.builder.DataBuilder;
import com.chuangke.common.db.page.CkPageQuery;
import com.chuangke.common.exception.ChuangkeException;
import com.chuangke.common.idgen.IdManager;
import com.chuangke.common.utils.SpringContextUtil;
import com.chuangke.file.model.FileInfo;
import com.chuangke.flow.constant.FlowConstant;
import com.chuangke.flow.dao.FlowMdAuditPointDao;
import com.chuangke.flow.dao.FlowMdFlowDao;
import com.chuangke.flow.dao.FlowMdNodeDao;
import com.chuangke.flow.dao.FlowMdNodeProcessorDao;
import com.chuangke.flow.domain.FlowMdNodeOrder;
import com.chuangke.flow.entity.FlowMdAuditPoint;
import com.chuangke.flow.entity.FlowMdFlow;
import com.chuangke.flow.entity.FlowMdNode;
import com.chuangke.flow.entity.FlowMdNodeProcessor;
import com.chuangke.flow.service.FlowDefineService;
import com.chuangke.framework.model.BasicModel;
import com.chuangke.framework.service.FlowPropertyExtendService;
import com.chuangke.regulation.entity.RgNkqxzyGwzz;
import com.chuangke.regulation.service.RgNkqxzyGwzzService;
import com.chuangke.system.entity.SysRelation;
import com.chuangke.system.service.SysRelationService;

import cn.hutool.core.util.StrUtil;

@Service
public class FlowDefineServiceImpl implements FlowDefineService {

	@Autowired
	private FlowMdFlowDao flowMdFlowDao;
	@Autowired
	private FlowMdNodeDao flowMdNodeDao;
	@Autowired
	private FlowMdNodeProcessorDao flowMdNodeProcessorDao;
	@Autowired
	private FlowMdAuditPointDao flowMdAuditPointDao;
	@Autowired
	private SysRelationService sysRelationService;
	@Autowired
	private AttachmentService attachmentService;
	@Autowired
	private RgNkqxzyGwzzService rgNkqxzyGwzzService ;

	@Override
	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = CacheKeyConstants.FLOW_MD_FLOW, key = "#flow.id")
	public FlowMdFlow save(FlowMdFlow flow) {
		//如果是修改需要先删除之前的流程
		if (StrUtil.isNotBlank(flow.getId())) {
			remove(Collections.singletonList(flow.getId()));
		} else {
			flow.setId(IdManager.nextId());
		}

		//初始化流程节点信息
		List<FlowMdNode> nodeList = flow.getNodes();
		nodeList.forEach(node -> node.setFlowId(flow.getId()));
		//初始化流程节点处理信息
		List<FlowMdNodeProcessor> processors = initProcessor(flow) ;
		//初始化岗位审核要点
		List<FlowMdAuditPoint >auditPoints = initAuditPoint(flow,processors) ;
		//初始化流程节点的附件和制度条款(历史处理，暂未启用)
		List<SysRelation> relationList = getRelationList(nodeList);
		//初始化岗位处理附件
		relationList.addAll(getRelationList4NodeProcessor(processors)) ;

		//保存流程
		flowMdFlowDao.save(flow);
		saveBatchIfNotEmpty(nodeList, flowMdNodeDao::saveBatch);
		saveBatchIfNotEmpty(processors, flowMdNodeProcessorDao::saveBatch);
		saveBatchIfNotEmpty(auditPoints, flowMdAuditPointDao::saveBatch);
		saveBatchIfNotEmpty(relationList, sysRelationService::saveBatch);

		return flow;
	}
	private void saveBatchIfNotEmpty(List list, Consumer<List> saveOperation) {
		if (!CollectionUtils.isEmpty(list)) {
			saveOperation.accept(list);
		}
	}

	/**
	 * 初始化流程节点处理信息
	 * @param flow
	 * @return
	 */
	private List<FlowMdNodeProcessor> initProcessor(FlowMdFlow flow){
		List<FlowMdNodeProcessor> processors = new ArrayList<>();
		flow.getNodes().forEach(node -> {
			if (node.getProcessors() != null) {
				processors.addAll(node.getProcessors());
			}
		});

		processors.forEach(processor ->{
			processor.setId(IdManager.nextId());
			processor.setFlowId(flow.getId()) ;
		});

		return processors ;
	}

	/**
	 * 初始化岗位审核要点
	 * @param flow
	 * @param processors
	 * @return
	 */
	private List<FlowMdAuditPoint> initAuditPoint(FlowMdFlow flow,List<FlowMdNodeProcessor> processors){
		List<FlowMdAuditPoint> auditPoints = new ArrayList<>();
		processors.forEach(processor ->{
			if (processor.getAuditPoints() != null) {
				processor.getAuditPoints().forEach(point -> {
					point.setId(IdManager.nextId());
					point.setFlowId(flow.getId());
					point.setNodeId(processor.getNodeId());
					point.setProcessorId(processor.getId()) ;
				});

				auditPoints.addAll(processor.getAuditPoints());
			}
		});
		return auditPoints ;
	}

	/**
	 * 初始化流程节点的附件和制度条款
	 * @param nodes
	 * @return
	 */
	private List<SysRelation> getRelationList(List<FlowMdNode> nodes) {
		List<SysRelation> relationList = new ArrayList<>();
		nodes.forEach(node -> {
			if (CollectionUtils.isNotEmpty(node.getRegulationItmes())) {
				node.getRegulationItmes().forEach(a -> {
					a.setId(IdManager.nextId());
					a.setSubjectId(node.getId());
					a.setRelationType(SysRelationTypeConstants.RG_REGULATION_ITME);
				});
				relationList.addAll(node.getRegulationItmes());
			}
			if (CollectionUtils.isNotEmpty(node.getAttachments())) {
				node.getAttachments().forEach(a -> {
					a.setId(IdManager.nextId());
					a.setSubjectId(node.getId());
					a.setRelationType(SysRelationTypeConstants.AFFIX);
				});
				relationList.addAll(node.getAttachments());
			}
		});
		return relationList;
	}

	/**
	 * 初始化岗位处理附件
	 * @param models
	 * @return
	 */
	private List<SysRelation> getRelationList4NodeProcessor(List<FlowMdNodeProcessor> models) {
		List<SysRelation> relationList = new ArrayList<>();
		models.forEach(model -> {
			if (CollectionUtils.isNotEmpty(model.getAttachments())) {
				model.getAttachments().forEach(a -> {
					a.setId(IdManager.nextId());
					a.setSubjectId(model.getId());
					a.setRelationType(SysRelationTypeConstants.AFFIX);
				});
				relationList.addAll(model.getAttachments());
			}
		});
		return relationList;
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void remove(List<String> ids) {
		flowMdFlowDao.removeByIds(ids);
		flowMdNodeDao.remove(new QueryWrapper<FlowMdNode>().lambda().in(FlowMdNode::getFlowId, ids));
		flowMdNodeProcessorDao
				.remove(new QueryWrapper<FlowMdNodeProcessor>().lambda().in(FlowMdNodeProcessor::getFlowId, ids));
		flowMdAuditPointDao.remove(new QueryWrapper<FlowMdAuditPoint>().lambda().in(FlowMdAuditPoint::getFlowId, ids));
		
		List<FlowMdNode> nodeList = flowMdNodeDao.list(new QueryWrapper<FlowMdNode>().lambda().in(FlowMdNode::getFlowId, ids));
		if(!CollectionUtils.isEmpty(nodeList)) {
			sysRelationService.deleteBySubjectIds(nodeList.stream().map(FlowMdNode::getId).collect(Collectors.toList()));
		}

		ids.forEach(id -> {
			((FlowDefineService) AopContext.currentProxy()).evictCache(id);
		});
	}

	@Override
	@CacheEvict(cacheNames = CacheKeyConstants.FLOW_MD_FLOW, key = "#id")
	public void evictCache(String id) {
		//没有实现，只为删除缓存
	}

	@Override
	public IPage<FlowMdFlow> page(CkPageQuery pageQuery) {
		String fuzzy = (String) pageQuery.get("fuzzy");
		QueryWrapper<FlowMdFlow> queryWrapper = new QueryWrapper<>();
		if (!StrUtil.isBlank(fuzzy)) {
			queryWrapper.lambda().like(FlowMdFlow::getId, fuzzy).or().like(FlowMdFlow::getTitle, fuzzy);
		}

		IPage<FlowMdFlow> result = flowMdFlowDao.page(pageQuery.getPage(), queryWrapper);
		DataBuilder.of(result.getRecords()).appendBuilder(new UserDataBuilder(),
				new DataBuildConfig("createdBy", "createdByName"), new DataBuildConfig("updatedBy", "updatedByName"))
				.build();
		return result;
	}

	@Override
	@Cacheable(cacheNames = CacheKeyConstants.FLOW_MD_FLOW, key = "#id")
	public FlowMdFlow load(String id) {
		FlowMdFlow flow = flowMdFlowDao.getById(id);

		if (flow == null) {
			throw new ChuangkeException("未找到流程配置信息");
		}

		//加载流程节点
		List<FlowMdNode> nodeList = flowMdNodeDao
				.list(new QueryWrapper<FlowMdNode>().lambda().eq(FlowMdNode::getFlowId, id));

		//加载流程节点Processor
		List<FlowMdNodeProcessor> processorList = flowMdNodeProcessorDao
				.list(new QueryWrapper<FlowMdNodeProcessor>().lambda().eq(FlowMdNodeProcessor::getFlowId, id));
		Map<String, List<FlowMdNodeProcessor>> map = processorList.stream()
				.collect(Collectors.groupingBy(FlowMdNodeProcessor::getNodeId));
		nodeList.forEach(node -> node.setProcessors(map.get(node.getId())));

		//加载流程节点Processor审核点及附件
		List<FlowMdAuditPoint> auditPoints = flowMdAuditPointDao
				.list(new QueryWrapper<FlowMdAuditPoint>().lambda().eq(FlowMdAuditPoint::getFlowId, id));
		Map<String, List<FlowMdAuditPoint>> pointMap = auditPoints.stream()
				.collect(Collectors.groupingBy(p -> String.format("%s-%s", p.getNodeId(), p.getProcessorId())));
		processorList.forEach(p ->{
			String key = String.format("%s-%s", p.getNodeId(), p.getId()) ;
			p.setAuditPoints(pointMap.get(key)) ;

			List<SysRelation> attachmentList = sysRelationService.findBySubjectId(p.getId());
			attachmentList.stream().forEach(sr -> {
				FileInfo fileInfo = attachmentService.findAttachmentMeta(sr.getRelationId());
				sr.setRelationName(fileInfo.getName());
			});
			p.setAttachments(attachmentList);
		});

		//加载审核点的内控属性
		if(CollectionUtils.isNotEmpty(auditPoints)){
			List<String> nkqxIdList = auditPoints.stream().map(FlowMdAuditPoint::getNkqxId).collect(Collectors.toList()) ;
			List<RgNkqxzyGwzz> nkqxList = rgNkqxzyGwzzService.findByIds(nkqxIdList) ;
			Map<String,RgNkqxzyGwzz> nkqxMap = nkqxList.stream().collect(Collectors.toMap(RgNkqxzyGwzz::getId, a -> a)) ;
			auditPoints.forEach(a -> {
				RgNkqxzyGwzz nqkx = nkqxMap.get(a.getNkqxId()) ;
				if(nqkx != null){
					a.setItemCode(nqkx.getItemCode());
					a.setItemName(nqkx.getItemName());
					a.setPostDescription(nqkx.getPostDescription());
					a.setNkqxName(nqkx.getPostDescription());
				}
			});
		}

		//加载节点的扩展属性（暂未启用）
		loadNodeExtendProperty(nodeList) ;

		flow.setNodes(nodeList);

		DataBuilder.of(processorList).appendBuilder(new DeptDataBuilder(), new DataBuildConfig("deptId", "deptName"))
				.appendBuilder(new RoleDataBuilder(), new DataBuildConfig("processId", "processName"))
				.build();

		return flow;
	}

	/**
	 * 加载节点的扩展属性
	 * @param nodeList
	 */
	private void loadNodeExtendProperty(List<FlowMdNode> nodeList) {
		if(CollectionUtils.isEmpty(nodeList)) {
			return ;
		}
		
		List<String> nodeIdList = nodeList.stream().map(FlowMdNode::getId).collect(Collectors.toList());
		List<SysRelation> relationList = sysRelationService.findBySubjectIds(nodeIdList);
		
		if(CollectionUtils.isEmpty(relationList)) {
			return ;
		}

        /*加载relationObject对象*/
		Map<String, List<SysRelation>> typeGroup = relationList.stream()
				.collect(Collectors.groupingBy(SysRelation::getRelationId));
		if(!CollectionUtils.isEmpty(typeGroup.get(SysRelationTypeConstants.RG_REGULATION_ITME))) {
			FlowPropertyExtendService service = (FlowPropertyExtendService) SpringContextUtil.getBean("flowNodeRegulationItemService") ;
			List<BasicModel> list = service.load(typeGroup.get(SysRelationTypeConstants.RG_REGULATION_ITME).stream().map(SysRelation::getRelationId).collect(Collectors.toList())) ;
			
			Map<String,BasicModel> dataMap = list.stream().collect(Collectors.toMap(BasicModel::getId, a -> a)) ;
			typeGroup.get(SysRelationTypeConstants.RG_REGULATION_ITME).forEach(relation -> relation.setRelationObject(dataMap.get(relation.getRelationId())));
		}
		if(!CollectionUtils.isEmpty(typeGroup.get(SysRelationTypeConstants.AFFIX))) {
			FlowPropertyExtendService service = (FlowPropertyExtendService) SpringContextUtil.getBean("flowNodeFileInfoService") ;
			List<BasicModel> list = service.load(typeGroup.get(SysRelationTypeConstants.AFFIX).stream().map(SysRelation::getRelationId).collect(Collectors.toList())) ;
			
			Map<String,BasicModel> dataMap = list.stream().collect(Collectors.toMap(BasicModel::getId, a -> a)) ;
			typeGroup.get(SysRelationTypeConstants.AFFIX).forEach(relation -> relation.setRelationObject(dataMap.get(relation.getRelationId())));
		}

        /*load扩展属性*/
		Map<String, List<SysRelation>> groupMap = relationList.stream()
				.collect(Collectors.groupingBy(SysRelation::getRelationId));

		for (FlowMdNode node : nodeList) {
			if (CollectionUtils.isEmpty(groupMap.get(node.getId()))) {
				continue;
			}

			Map<String, List<SysRelation>> groupTypeMap = groupMap.get(node.getId()).stream()
					.collect(Collectors.groupingBy(SysRelation::getRelationType));
			node.setAttachments(groupTypeMap.get(SysRelationTypeConstants.AFFIX));
			node.setRegulationItmes(groupTypeMap.get(SysRelationTypeConstants.RG_REGULATION_ITME) == null ? null
					: groupTypeMap.get(SysRelationTypeConstants.RG_REGULATION_ITME));
		}
	}

	/* 获取流程加载顺序列表 */

	/**
	 * 加载流程的顺序审批列表
	 */
	@Override
	public List<FlowMdNodeOrder> getNodeListByOrder(String flowId) {
		List<FlowMdNodeOrder> orderList = getOrderList(flowId);

		// 最后一个审批节点的父级是起始节点，所以需要把分级树的所有末节点增加最后一个节点
		boolean isHandleLastNode = isHandleLastNode(orderList);

		// 获取最后的节点
		FlowMdNodeOrder lastNode = null;
		if (isHandleLastNode) {
			lastNode = getLastNode(orderList);
		}

		// 审批流进行构建树形和分级
		orderList = buildTree(orderList);
		List<FlowMdNodeOrder> orderListWithLevelInfo = new ArrayList<>();
		level(0, orderList, orderListWithLevelInfo);

		List<FlowMdNodeOrder> lastList = null;
		if (isHandleLastNode) {
			lastList = handleLasNode(orderListWithLevelInfo, lastNode);
		}

		lastList = lastList.stream().filter(fmno -> !StrUtil.isBlank(fmno.getProcessorId())
				|| FlowConstant.NODE_TYPE_START.equals(fmno.getNodeType())).collect(Collectors.toList());

		Map<String, FlowMdNodeOrder> resultMap = new HashMap<>();
		Map<String, Integer> orderMap = new HashMap<>();
		for (FlowMdNodeOrder order : lastList) {
			if (!orderMap.containsKey(order.getProcessorId())) {
				resultMap.put(order.getProcessorId(), order);
				orderMap.put(order.getProcessorId(), order.getOrder());
			} else {
				int orderNum = orderMap.get(order.getProcessorId());
				FlowMdNodeOrder cacheOrder = resultMap.get(order.getProcessorId());
				cacheOrder.setOrder(Math.max(orderNum, cacheOrder.getOrder()));
				cacheOrder.getNodeIdList().add(order.getId());
			}
		}
		return new ArrayList<>(resultMap.values());
	}

	private List<FlowMdNodeOrder> getOrderList(String flowId) {
		List<FlowMdNode> nodeList = flowMdNodeDao.list(
				Wrappers.<FlowMdNode>lambdaQuery().eq(FlowMdNode::getFlowId, flowId).orderByAsc(FlowMdNode::getId));

		List<FlowMdNodeProcessor> nodeProcessorList = flowMdNodeProcessorDao
				.list(Wrappers.<FlowMdNodeProcessor>lambdaQuery().eq(FlowMdNodeProcessor::getFlowId, flowId));

		Map<String, FlowMdNodeProcessor> nodeProcessorMap = nodeProcessorList.stream()
				.collect(Collectors.toMap(FlowMdNodeProcessor::getNodeId, a -> a));

		List<FlowMdNodeOrder> orderList = new ArrayList<>();
		for (FlowMdNode node : nodeList) {
			FlowMdNodeProcessor processor = nodeProcessorMap.get(node.getId());
			FlowMdNodeOrder fmno = new FlowMdNodeOrder();
			fmno.setId(node.getId());
			fmno.setParentId(node.getPrevId());
			fmno.setTitle(node.getTitle());
			fmno.setNodeType(node.getType());
			fmno.setOrder(0);
			fmno.getNodeIdList().add(node.getId());
			if (processor != null) {
				fmno.setProcessorId(processor.getProcessType() + "-" + processor.getProcessId());
			}

			orderList.add(fmno);
		}
		return orderList;
	}

	/**
	 * 最后一个审批节点的父级是起始节点，所以需要把分级树的所有末节点增加最后一个节点; 只有存在分支节点的流程才需要处理。
	 *
	 * @param orderList
	 * @return
	 */
	private boolean isHandleLastNode(List<FlowMdNodeOrder> orderList) {
		List<FlowMdNodeOrder> conditionList = orderList.stream()
				.filter(node -> FlowConstant.NODE_TYPE_CONDITION.equals(node.getNodeType())).collect(Collectors.toList());

		return !CollectionUtils.isEmpty(conditionList);
	}

	private List<FlowMdNodeOrder> handleLasNode(List<FlowMdNodeOrder> orderListWithLevelInfo,
			FlowMdNodeOrder lastNode) {
		List<FlowMdNodeOrder> lastList = new ArrayList<>();

		for (FlowMdNodeOrder node : orderListWithLevelInfo) {
			if (node.getId().equals(lastNode.getId())) {
				continue;
			}
			lastList.add(node);
			if (Masks.BOOL_YES.equals(node.getIsLastLevel())) {
				node.setIsLastLevel(Masks.BOOL_NO);

				FlowMdNodeOrder childNode = copyNode(lastNode);
				childNode.setIsLastLevel(Masks.BOOL_YES);
				childNode.setLevel(node.getLevel() + 1);
				childNode.setOrder(childNode.getLevel());

				lastList.add(childNode);
			}
		}

		return lastList;
	}

	private FlowMdNodeOrder copyNode(FlowMdNodeOrder node) {
		FlowMdNodeOrder newNode = new FlowMdNodeOrder();
		BeanUtils.copyProperties(node, newNode);
		return newNode;
	}

	private FlowMdNodeOrder getLastNode(List<FlowMdNodeOrder> nodeList) {
		List<FlowMdNodeOrder> startNodeList = nodeList.stream()
				.filter(node -> FlowConstant.NODE_TYPE_START.equals(node.getNodeType())).collect(Collectors.toList());
		if (CollectionUtils.isEmpty(startNodeList)) {
			throw new ChuangkeException("未找到起始节点");
		}
		if (startNodeList.size() != 1) {
			throw new ChuangkeException(String.format("寻找起始节点失败，找到%s个起始节点", startNodeList.size()));
		}
		FlowMdNodeOrder startNode = startNodeList.get(0);

		List<FlowMdNodeOrder> lastNodeList = nodeList.stream()
				.filter(node -> FlowConstant.NODE_TYPE_APPROVER.equals(node.getNodeType())
						&& startNode.getId().equals(node.getParentId()))
				.collect(Collectors.toList());

		if (CollectionUtils.isEmpty(lastNodeList)) {
			throw new ChuangkeException("未找到结束节点");
		}
		if (lastNodeList.size() != 1) {
			throw new ChuangkeException(String.format("寻找结束节点失败，找到%s个结束节点", lastNodeList.size()));
		}
		return lastNodeList.get(0);
	}

	private List<FlowMdNodeOrder> buildTree(List<FlowMdNodeOrder> list) {
		list.sort((dept1, dept2) -> {
			if (dept1.getId().length() != dept2.getId().length()) {
				return dept1.getId().length() - dept2.getId().length();
			}
			return dept1.getId().compareTo(dept2.getId());
		});

		List<FlowMdNodeOrder> tree = new ArrayList<>();
		for (FlowMdNodeOrder bean : list) {
			if (bean.getParentId() == null || bean.getParentId().trim().isEmpty() || "0".equals(bean.getParentId())) {
				tree.add(bean);
			}
		}
		findChildren(tree, list);
		return tree;
	}

	private void findChildren(List<FlowMdNodeOrder> tree, List<FlowMdNodeOrder> lists) {
		for (FlowMdNodeOrder treeBean : tree) {
			List<FlowMdNodeOrder> children = new ArrayList<>();
			for (FlowMdNodeOrder bean : lists) {
				if (treeBean.getId() != null && treeBean.getId().equals(bean.getParentId())) {
					children.add(bean);
				}
			}
			treeBean.setChildren(children);
			findChildren(children, lists);
		}
	}

	private void level(int parenLevel, List<FlowMdNodeOrder> deptList, List<FlowMdNodeOrder> newList) {
		for (FlowMdNodeOrder fmno : deptList) {
			fmno.setLevel(parenLevel + 1);
			fmno.setOrder(fmno.getLevel());
			fmno.setIsLastLevel(CollectionUtils.isEmpty(fmno.getChildren()) ? "1" : "0");
			newList.add(fmno);
			level(fmno.getLevel(), fmno.getChildren(), newList);
		}
	}

}
